/*

 * Copyright (c) 2017-2018, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package of.hotswapinject.plugin.imp;

import of.hotswapinject.core.common.RuntimeContext;
import of.hotswapinject.core.plugin.*;
import of.hotswapinject.plugin.imp.perf.PerfStack;
import of.hotswapinject.plugin.imp.perf.MethodCallElapse;
import of.hotswapinject.plugin.imp.perf.PerfStatReporter;
import org.apache.commons.cli.Option;

public class MethodPerfMoniter extends MethodFilterBase {
    private static ThreadLocal<PerfStack<MethodCallElapse>> map = new ThreadLocal<PerfStack<MethodCallElapse>>();

    private static final Option perfMoniter;
    static {
        perfMoniter = new Option("m", "perfMoniter", false, "moniter method perf");
        perfMoniter.setRequired(false);
    }


    @Override
    protected boolean canHandle(String key, MethodMeta adviceAdapter) {
        return true;
    }

    @Override
    protected boolean hookExitOnException(MethodMeta adviceAdapter) {
        return true;
    }



    private PerfStack<MethodCallElapse> getStack(){
        PerfStack<MethodCallElapse> stack = map.get();
        if(stack == null) {
            stack = new PerfStack<MethodCallElapse>();
            map.set(stack);
        }
        return stack;
    }

    @Override
    public IMethodEnterPlugin.ReturnParam onEnter(String key, Object[] params) {
        MethodCallElapse perfStatCounter = new MethodCallElapse(key);
        getStack().push(perfStatCounter);
        return IMethodEnterPlugin.ReturnParam.none();
    }

    @Override
    public IMethodExitPlugin.ReturnParam onExit(String key, Object[] params, Object result) {
        PerfStack<MethodCallElapse> stack = getStack();
        if(stack.empty()) {
            return IMethodExitPlugin.ReturnParam.none();
        }
        MethodCallElapse perfStatCounter = stack.pop();
        perfStatCounter.endMethod();

        if(!stack.empty()) {
            MethodCallElapse parent = stack.peek();
            perfStatCounter.calucteParentElapse(parent);
        }

        stack.endMethod(perfStatCounter.getKey(), perfStatCounter.getExcludeElapse());
        if(stack.empty()) {
            new PerfStatReporter().send(stack);
        }
        return IMethodExitPlugin.ReturnParam.none();
    }

    @Override
    public Option getOption() {
        return perfMoniter;
    }
}
